﻿

#region Using Statements

// System References
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading;

// XNA References
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

// Internal References
using Freeform.Framework.Graphics;
using Freeform.Framework.Utility;
using Freeform.Framework.Input;
using Freeform.Framework.Geometry;
using Freeform.Framework.Geometry.Collision;
using Game = Freeform.Framework.Game;

#endregion


namespace Freeform.Framework
{
    /// <summary>
    /// Entities are renderable Elements that make up a scene in a world.
    /// </summary>
    public class Entity : Element
    {

        #region Events

        /// <summary>
        /// This event is fired before any draw routines are done.
        /// </summary>
        public event EventHandler DrawBeginning;


        /// <summary>
        /// This event is fired when the entity has finished drawing.
        /// </summary>
        public event EventHandler DrawEnded;

        #endregion


        #region Members

        /// <summary>
        /// Gets the world that this entity renders to.
        /// </summary>
        public World World { get; private set; }


        /// <summary>
        /// Gets the spatial properties of the entity including position, direction and size.
        /// </summary>
        public SpatialProperties SpatialProperties { get; private set; }


        /// <summary>
        /// Gets or sets how long this entity has been active for (in milliseconds.)
        /// </summary>
        public UInt32 Age { get; set; }


        /// <summary>
        /// Gets the actor for this entity.
        /// </summary>
        public Actor Actor { get; set; }


        /// <summary>
        /// Gets the collision properties for this entity.
        /// </summary>
        public CollisionProperties CollisionProperties { get; private set; }

        #endregion


        #region Init

        /// <summary>
        /// Creates a new entity and automatically links it to the given world.
        /// </summary>
        /// <param name="World">The world that this entity belongs to.</param>
        public Entity(World World) 
        {
            this.World = World;
            World.RegisterEntity(this);
            SpatialProperties = new SpatialProperties();
            Age = 0;
            CollisionProperties = new CollisionProperties(this);
        }

        #endregion


        #region Update

        /// <summary>
        /// Allows the entity to update itself.
        /// </summary>
        /// <param name="time">Provides a snapshot of timing values.</param>
        protected override void BeginUpdate(GameTime Time)
        {
            Age += (UInt32)Time.ElapsedGameTime.Milliseconds;

            if (SpatialProperties.MotionController != null)
                SpatialProperties.MotionController.Update(Time);

            base.BeginUpdate(Time);
        }

        #endregion


        #region Draw

        /// <summary>
        /// Allows the entity to draw itself.
        /// </summary>
        /// <param name="Time">Provides a snapshot of timing values.</param>
        /// <param name="Viewport">The viewport to render to.</param>
        public void Draw(GameTime Time, Viewport2D Viewport)
        {
            // Fire drawing event.
            if (DrawBeginning != null) DrawBeginning(this, new EventArgs());

            // Call begin draw routine.
            BeginDraw(Time, Viewport);

            // Call main draw routine.
            DoDraw(Time, Viewport);

            // Call finalise draw routine.
            EndDraw(Time, Viewport);

            // Fire drawing finished event.
            if (DrawEnded != null) DrawEnded(this, new EventArgs());
        }


        /// <summary>
        /// Conduct any pre-drawing routines here.
        /// </summary>
        /// <param name="Time">Provides a snapshot of timing values.</param>
        /// <param name="Viewport">The Viewport to draw to.</param>
        protected virtual void BeginDraw(GameTime Time, Viewport2D Viewport) { }


        /// <summary>
        /// Conduct main drawing routine here.
        /// </summary>
        /// <param name="Time">Provides a snapshot of timing values.</param>
        /// <param name="Viewport">The viewport to draw to.</param>
        protected virtual void DoDraw(GameTime Time, Viewport2D Viewport) 
        {
            if (Actor != null && Actor.IsVisible)
                Actor.Draw(Viewport, Time);
        }


        /// <summary>
        /// Conduct any post drawing actions here.
        /// </summary>
        /// <param name="Time">Provides a snapshot of timing values.</param>
        /// <param name="Viewport">The viewport to draw to.</param>
        protected virtual void EndDraw(GameTime Time, Viewport2D Viewport) { }

        #endregion


        #region Dispose

        /// <summary>
        /// Disposes the entity and removes it from the world.
        /// </summary>
        protected override void OnDispose()
        {
            base.OnDispose();

            World.UnregisterEntity(this);
        }

        #endregion


        #region Utility

        /// <summary>
        /// Gets the location as a factor of game-screen resolution of this entity.
        /// </summary>
        /// <param name="viewport">The viewport with which we wish to look through.</param>
        /// <returns>The screen coordinates as a factor of game-screen resolution.</returns>
        public virtual Vector2 GetScreenLocation(Viewport2D viewport)
        {
            return viewport.SpatialProperties.Position + (SpatialProperties.Position - viewport.Camera.SpatialProperties.Position) / viewport.Camera.SpatialProperties.Size * viewport.SpatialProperties.Size;
        }


        
        public virtual Boolean IsMouseOver(Vector2 sizeScaling)
        {
            for (int i = 0; i < World.Viewports.Count; i++)
            {
                // Check if the mouse is over this viewport.
                if (World.Viewports[i].IsMouseOver)
                {
                    // Find the coordinates of the mouse in world units.
                    Vector2 MouseLoc = World.Viewports[i].MouseLocation_World;

                    // Check if the mouse is over our entity.
                    if (MouseLoc.X > SpatialProperties.Position.X - SpatialProperties.Size.X * sizeScaling.X / 2 &&
                        MouseLoc.Y > SpatialProperties.Position.Y - SpatialProperties.Size.Y * sizeScaling.Y / 2 &&
                        MouseLoc.X < SpatialProperties.Position.X + SpatialProperties.Size.X * sizeScaling.X / 2 &&
                        MouseLoc.Y < SpatialProperties.Position.Y + SpatialProperties.Size.Y * sizeScaling.Y / 2)
                        return true;
                }
            }
            return false;
        }


        
        public virtual Boolean IsMouseOver()
        {
            for (int i = 0; i < World.Viewports.Count; i++)
            {
                // Check if the mouse is over this viewport.
                if (World.Viewports[i].IsMouseOver)
                {
                    // Find the coordinates of the mouse in world units.
                    Vector2 MouseLoc = World.Viewports[i].MouseLocation_World;

                    // Check if the mouse is over our entity.
                    if (MouseLoc.X > SpatialProperties.Position.X - SpatialProperties.Size.X / 2 &&
                        MouseLoc.Y > SpatialProperties.Position.Y - SpatialProperties.Size.Y / 2 &&
                        MouseLoc.X < SpatialProperties.Position.X + SpatialProperties.Size.X / 2 &&
                        MouseLoc.Y < SpatialProperties.Position.Y + SpatialProperties.Size.Y / 2)
                        return true;
                }
            }
            return false;
        }

        #endregion

    }
}